//========================================================= 
/**@file Tank.h
 * @brief 
 * 
 * @date 2025-07-21   11:38:01
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_TANK_H_
#define _LIBZHOUYB_TANK_H_
//---------------------------------------------------------
#include "TEngine.h"

#include <random>
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace game {
namespace tank90 {
//--------------------------------------------------------- 
enum enum_TankMask
{
    mask_None = 0,
    mask_Barrier = 1,
    mask_Bullet = 2,
    mask_Enemy = 4,
    mask_Player = 8,
    mask_Reward = 16,
};

enum enum_TankGroup
{
    group_Border = 0,
    group_A,
};

enum enum_TankMove
{
    move_None = 0,
    move_Left,
    move_Right,
    move_Up,
    move_Down,
};
//--------------------------------------------------------- 
#define TANK_CELL_SIZE 100
#define TANK_BULLET_SIZE 40
#define TANK_UNIT_SIZE 2 * TANK_CELL_SIZE

#define TANK_NAVIGATION_INTERVAL 100
#define TANK_SHOOT_INTERVAL 50
#define TANK_MOVE_SPEED 30
#define TANK_BULLET_SPEED 60
#define TANK_POWER_SPEED 10
#define TANK_MOVE_SLIDE_TIMEOUT 5
#define TANK_DEFAULT_HP 2
#define TANK_DEFAULT_MAGAZINE_CLIP 2
//--------------------------------------------------------- 
enum TankEvent
{
    e_None = 0,
    e_QueueFree,
    e_HitSomething,
    e_UseReward,
    e_Shoot,
    e_Navigation,
    e_Die,
};
//--------------------------------------------------------- 
/// 事件通知
struct ITankEvent
{
    virtual void OnEvent(uint event_id, TCharacter2D* body, uint value, pointer param) = 0;
};
/// 快照序列化
struct ITankSerialize
{
    virtual void OnSerialize(BinaryEncoder& se) const = 0;
    virtual void OnDeserialize(BinaryDecoder& se) = 0;
};
//--------------------------------------------------------- 
/// 提供基本功能
class TNode2D : public TCharacter2D, public ITankSerialize
{
public:
    TNode2D()
    {
        size = TVector2(TANK_CELL_SIZE, TANK_CELL_SIZE);
        layer = mask_Barrier;
        group = group_Border;
    }

    Ref<ITankEvent> Game;

    /// 每一帧触发
    virtual void OnFrame() {}
    /// 自身撞到其他对象时触发
    virtual void OnCollision(list_t<TCollisionData2D>& data) {}
    /// 被其他对象撞到时触发
    virtual void OnHurt(TCollisionData2D& data) {}

    virtual void OnSerialize(BinaryEncoder& se) const
    {
        se << position.x << position.y << size.x << size.y << layer << mask << group << direction.x << direction.y << speed;
    }
    virtual void OnDeserialize(BinaryDecoder& se)
    {
        se << position.x << position.y << size.x << size.y << layer << mask << group << direction.x << direction.y << speed;
    }

    void QueueFree()
    {
        Game->OnEvent(e_QueueFree, this, 0, NULL);
    }
};
//--------------------------------------------------------- 
/// 坦克基础逻辑
class Tank : public TNode2D, public RefObject
{
public:
    /// 对象ID
    uint Uid;
    /// 生命值
    uint Hp;
    /// 子弹等级
    uint Power;
    /// 子弹上限
    uint MagazineClip;
public:
    Tank()
    {
        size = TVector2(TANK_UNIT_SIZE, TANK_UNIT_SIZE);
        direction = TVector2_UP;

        Uid = 0;
        Hp = TANK_DEFAULT_HP;
        Power = 0;
        MagazineClip = TANK_DEFAULT_MAGAZINE_CLIP;
    }
    /// 转向
    void Turn(TVector2 d)
    {
        direction = d;
    }
    void Shoot()
    {
        Game->OnEvent(e_Shoot, this, 0, NULL);
    }
    virtual void OnHurt(TCollisionData2D& data)
    {
        Hp -= 1;
        if (Hp < 1)
        {
            Game->OnEvent(e_Die, this, 0, NULL);
            QueueFree();
        }
    }

    virtual void OnCollision(list_t<TCollisionData2D>& data)
    {
        for (list_t<TCollisionData2D>::iterator itr = data.begin(); itr != data.end(); ++itr)
        {
            if (itr->body->layer == mask_Reward)
            {
                TCharacter2D* node = (TCharacter2D*)itr->body;
                Game->OnEvent(e_UseReward, node, 0, itr->self);
            }
        }
    }
    virtual void OnSerialize(BinaryEncoder& se) const
    {
        TNode2D::OnSerialize(se);
        se << Uid << Hp << Power << MagazineClip;
    }
    virtual void OnDeserialize(BinaryDecoder& se)
    {
        TNode2D::OnDeserialize(se);
        se << Uid << Hp << Power << MagazineClip;
    }
};
//--------------------------------------------------------- 
/// 子弹逻辑
class TankBullet : public TNode2D
{
public:
    uint owner_uid;
    Ref<Tank> Owner;
public:
    TankBullet()
    {
        size = TVector2(TANK_BULLET_SIZE, TANK_BULLET_SIZE);

        layer = mask_Bullet;
        mask = mask_Barrier | mask_Bullet | mask_Enemy | mask_Player;

        owner_uid = 0;
    }
    virtual void OnCollision(list_t<TCollisionData2D>& data)
    {
        for (list_t<TCollisionData2D>::iterator itr = data.begin(); itr != data.end(); ++itr)
        {
            TNode2D* node = (TNode2D*)itr->body;
            node->OnHurt(*itr);
        }
        Game->OnEvent(e_HitSomething, this, 0, NULL);
        QueueFree();
    }

    virtual void OnSerialize(BinaryEncoder& se) const
    {
        TNode2D::OnSerialize(se);
        se << owner_uid;
    }
    virtual void OnDeserialize(BinaryDecoder& se)
    {
        TNode2D::OnDeserialize(se);
        se << owner_uid;
    }
};
//--------------------------------------------------------- 
/// 奖励道具逻辑
class TankReward : public TNode2D
{
public:
    TankReward()
    {
        size = TVector2(TANK_UNIT_SIZE, TANK_UNIT_SIZE);
        layer = mask_Reward;
        mask = mask_Player;
    }

    /// 应用效果
    virtual void Apply(Tank& tank) {}
};
//--------------------------------------------------------- 
/// 敌人逻辑
class TankEnemy : public Tank
{
protected:
    TickTimer _navigation_timer;
    TickTimer _shoot_timer;
public:
    TankEnemy()
    {
        size = TVector2(TANK_UNIT_SIZE, TANK_UNIT_SIZE);
        layer = mask_Enemy;
        mask = mask_Player | mask_Enemy | mask_Barrier;

        speed = TANK_MOVE_SPEED;
        _navigation_timer.SetTimeout(TANK_NAVIGATION_INTERVAL);
        _shoot_timer.SetTimeout(TANK_SHOOT_INTERVAL);
    }

    virtual void OnFrame()
    {
        Tank::OnFrame();

        if (_navigation_timer.Elapsed())
        {
            _navigation_timer.Restart();
            Game->OnEvent(e_Navigation, this, 0, NULL);
        }
        if (_shoot_timer.Elapsed())
        {
            _shoot_timer.Restart();
            Shoot();
        }
    }
    virtual void OnCollision(list_t<TCollisionData2D>& data)
    {
        for (list_t<TCollisionData2D>::iterator itr = data.begin(); itr != data.end(); ++itr)
        {
            if (itr->body->layer == mask_Barrier)
            {
                Game->OnEvent(e_Navigation, this, 0, NULL);
                break;
            }
        }
    }

    virtual void OnSerialize(BinaryEncoder& se) const
    {
        TNode2D::OnSerialize(se);
        se << _navigation_timer.TickCount();
    }
    virtual void OnDeserialize(BinaryDecoder& se)
    {
        TNode2D::OnDeserialize(se);
        uint tick = 0;
        se << tick;
        _navigation_timer.Forward(tick);
    }
};
//--------------------------------------------------------- 
/// 可操控的玩家
class TankPlayer : public Tank
{
protected:
    TickTimer _move_timer;
    uint _move_speed;
public:
    TankPlayer()
    {
        size = TVector2(TANK_UNIT_SIZE, TANK_UNIT_SIZE);
        layer = mask_Player;
        mask = mask_Player | mask_Enemy | mask_Barrier | mask_Reward;

        _move_speed = TANK_MOVE_SPEED;
        _move_timer.SetTimeout(TANK_MOVE_SLIDE_TIMEOUT);
    }

    /// 移动
    void Move(TVector2 d)
    {
        if (d.IsZero())
        {
            // 按键抬起后继续移动一段距离
            if (_move_timer.Elapsed())
            {
                speed = 0;
            }
        }
        else
        {
            if (speed > 0)
            {
                // 转向后立即停止
                if (direction != d)
                {
                    speed = 0;
                    Turn(d);
                }
                else
                {
                    _move_speed = speed;
                    _move_timer.Restart();
                }
            }
            else
            {
                // 静止到移动
                speed = _move_speed;
                direction = d;
                _move_timer.Restart();
            }
        }
    }
    virtual void OnHurt(TCollisionData2D& data)
    {
        // 无敌
    }

    virtual void OnSerialize(BinaryEncoder& se) const
    {
        TNode2D::OnSerialize(se);
        se << _move_timer.TickCount() << _move_speed;
    }
    virtual void OnDeserialize(BinaryDecoder& se)
    {
        TNode2D::OnDeserialize(se);
        uint tick = 0;
        se << tick << _move_speed;
        _move_timer.Forward(tick);
    }
};
//--------------------------------------------------------- 
struct ITankFactory
{
    virtual TNode2D* Create(uint class_type) = 0;
    virtual void Destroy(TNode2D* node) = 0;
};
//--------------------------------------------------------- 
/**
 * @brief 游戏逻辑部分
 * @date 2025-07-22 16:26
 */
class TankGame : public TEngine, public ITankEvent, public RefObject
{
protected:
    uint _cell_w;
    uint _cell_h;
    uint _random_seed;
    uint _random_count;

    std::mt19937 _random_gen;
    std::uniform_int_distribution<uint> _random;

    Ref<ITankFactory> _factory;
    std::vector<TankPlayer*> _players;

    uint _GetRandom()
    {
        ++_random_count;
        return _random(_random_gen);
    }
    TVector2 _GetRandomVector2()
    {
        const TVector2 all_directions[] = { TVector2_LEFT, TVector2_RIGHT, TVector2_UP, TVector2_DOWN };

        uint index = _GetRandom() % 4;
        return all_directions[index];
    }
    void _Init(uint w, uint h, Ref<ITankFactory> factory, uint rd_seed, uint rd_count)
    {
        _cell_w = w;
        _cell_h = h;

        _random_seed = rd_seed ? rd_seed : std::random_device()();
        _random_gen.seed(_random_seed);
        for (uint i = 0; i < rd_count; ++i)
        {
            _GetRandom();
        }

        _players.clear();
        _factory = factory;
    }
public:
    TankGame() : _random(0, 100)
    {
        _cell_h = 0;
        _cell_w = 0;
        _random_seed = 0;
        _random_count = 0;
    }
    virtual ~TankGame()
    {
        Fini();
    }

    /**
     * @brief 初始化地图数据
     * @param [in] uint w 地图宽(格子数)
     * @param [in] uint h 地图高(格子数)
     * @return void
     */
    void Init(uint w, uint h, Ref<ITankFactory> factory, uint rd_seed = 0, uint rd_count = 0)
    {
        Fini();

        _Init(w, h, factory, rd_seed, rd_count);

        // 绘制边框
        Place(Instantiate(mask_Barrier), 1, 0, w, 1);
        Place(Instantiate(mask_Barrier), 1, h + 1, w, 1);

        Place(Instantiate(mask_Barrier), 0, 1, 1, h);
        Place(Instantiate(mask_Barrier), w + 1, 1, 1, h);
    }

    void Fini()
    {
        for (list_t<TBody2D*>::iterator itr = _bodies.begin(); itr != _bodies.end(); ++itr)
        {
            _observer->OnFini(*itr);
            _factory->Destroy((TNode2D*)*itr);
        }
        _bodies.clear();

        _players.clear();
        _factory.Free();
    }

    /// 通过快照接口生成对象
    TNode2D* Instantiate(uint class_type)
    {
        TNode2D* node = _factory->Create(class_type);
        if (node)
        {
            node->Game = (*this);
            Append(node);
            if (node->layer == mask_Player)
            {
                _players.push_back((TankPlayer*)node);
            }
        }
        return node;
    }

    TNode2D* Place(TNode2D* node, int x, int y, uint w = 0, uint h = 0)
    {
        node->position.x = x * TANK_CELL_SIZE;
        node->position.y = y * TANK_CELL_SIZE;
        if (w && h)
        {
            node->size = TVector2(w, h) * TANK_CELL_SIZE;
        }
        return node;
    }

    void OnShoot(Tank* body)
    {
        if (body->MagazineClip < 1)
            return;
        TankBullet* bullet = (TankBullet*)Instantiate(mask_Bullet);
        if (bullet)
        {
            TVector2 half_size = bullet->size / 2;
            TVector2 margin = TVector2(body->direction.x * half_size.x, body->direction.y * half_size.y);
            bullet->owner_uid = body->Uid;
            bullet->Owner = *body;
            bullet->position = body->Center() - half_size + body->direction * TANK_CELL_SIZE + margin;
            bullet->speed = body->Power * TANK_POWER_SPEED + TANK_BULLET_SPEED;
            bullet->direction = body->direction;
            bullet->mask &= ~(body->layer);
            body->MagazineClip -= 1;
        }
    }

    void OnNavigation(Tank* body)
    {
        body->Turn(_GetRandomVector2());
    }

    void OnReward(TankReward* body, Tank* tank)
    {
        body->Apply(*tank);
        body->QueueFree();
    }

    void OnHitSomething(TankBullet* body)
    {
        if (body->Owner.IsNull())
        {
            for (list_t<TBody2D*>::iterator itr = _bodies.begin(); itr != _bodies.end(); ++itr)
            {
                if ((*itr)->layer == mask_Player || (*itr)->layer == mask_Enemy)
                {
                    Tank* tank = (Tank*)(*itr);
                    if (tank->Uid == body->owner_uid)
                    {
                        body->Owner = *tank;
                        break;
                    }
                }
            }
        }
        if (body->Owner)
        {
            body->Owner->MagazineClip += 1;
        }
    }

    void OnDie(Tank* body)
    {
        // 在顶部随机一个格子生成敌人
        uint cell_x = _GetRandom() % (_cell_w - 1);
        Place(Instantiate(mask_Enemy), cell_x + 1, 1);
    }

    TankPlayer* AcceptInput(uint tank_id, byte direction, byte key_bit_code)
    {
        for (int i = 0; i < _players.size(); ++i)
        {
            TankPlayer* p = _players[i];
            if (p->Uid == tank_id)
            {
                AcceptInput(p, direction, key_bit_code);
                return p;
            }
        }
        return NULL;
    }
    void AcceptInput(TankPlayer* player, byte direction, byte key_bit_code)
    {
        TVector2 d = TVector2_ZERO;
        switch (direction)
        {
        case move_Left:
            d = TVector2_LEFT;
            break;
        case move_Right:
            d = TVector2_RIGHT;
            break;
        case move_Up:
            d = TVector2_UP;
            break;
        case move_Down:
            d = TVector2_DOWN;
            break;
        }
        player->Move(d);
        if (key_bit_code)
        {
            player->Shoot();
        }
    }

    virtual void OnEvent(uint event_id, TCharacter2D* body, uint value, pointer param)
    {
        switch (event_id)
        {
        case e_QueueFree:
            QueueFree(body);
            break;
        case e_Navigation:
            OnNavigation((Tank*)body);
        case e_Shoot:
            OnShoot((Tank*)body);
            break;
        case e_UseReward:
            OnReward((TankReward*)body, (Tank*)param);
            break;
        case e_HitSomething:
            OnHitSomething((TankBullet*)body);
            break;
        case e_Die:
            OnDie((Tank*)body);
            break;
        }
    }

    /// 生成快照数据
    static void SaveSnapshoot(TankGame& game, ByteBuilder& data)
    {
        BinaryEncoder se(data);
        se << game._cell_w << game._cell_h << game._random_seed << game._random_count << static_cast<int>(game._bodies.size());

        for (list_t<TBody2D*>::iterator itr = game._bodies.begin(); itr != game._bodies.end(); ++itr)
        {
            TNode2D* node = (TNode2D*)(*itr);
            // class_type
            se << node->layer;
            node->OnSerialize(se);
        }
    }
    /// 加载快照数据
    static void LoadSnapshoot(const ByteArray& data, TankGame& game, Ref<ITankFactory> factory)
    {
        BinaryDecoder se(data);
        int count = 0;
        uint w = 0;
        uint h = 0;
        uint rd_seed = 0;
        uint rd_count = 0;
        se << w << h << rd_seed << rd_count << count;

        game.Fini();
        game._Init(w, h, factory, rd_seed, rd_count);

        for (int i = 0; i < count; ++i)
        {
            uint class_type = mask_None;
            se << class_type;

            TNode2D* node = game.Instantiate(class_type);
            if (node)
            {
                game._Insert();
                node->OnDeserialize(se);
            }
        }
    }
};
//--------------------------------------------------------- 
enum enum_TankAction
{
    action_Known = 0,
    action_Input,
};

struct TankAction
{
    uint action;
    uint tank_uid;

    TankAction() : action(action_Known) {}
    virtual ~TankAction() {}

    virtual void OnSerialize(BinaryEncoder& se) const
    {
        se << tank_uid;
    }
    virtual void OnDeserialize(BinaryDecoder& se)
    {
        se << tank_uid;
    }

    virtual void DoAction(TankGame& game) {}
};
//--------------------------------------------------------- 
struct TankInputAction : public TankAction
{
    byte direction;
    byte key_bit_code;

    TankInputAction()
    {
        action = action_Input;
    }

    virtual void OnSerialize(BinaryEncoder& se) const
    {
        TankAction::OnSerialize(se);
        se << direction << key_bit_code;
    }
    virtual void OnDeserialize(BinaryDecoder& se)
    {
        TankAction::OnDeserialize(se);
        se << direction << key_bit_code;
    }

    virtual void DoAction(TankGame& game)
    {
        game.AcceptInput(tank_uid, direction, key_bit_code);
    }
};
//--------------------------------------------------------- 
struct TankFrame
{
    uint frameId;
    list_t<TankAction*> actions;
    ByteBuilder snapshoot;
};
//--------------------------------------------------------- 
class TankRecorder
{
protected:
    uint _snapshoot_step;
    list_t<TankFrame> _frames;
    uint _frame_id;
    list_t<TankFrame>::iterator _current;
    list_t<TankFrame>::iterator _replay;

    void _yield()
    {
        _current = _frames.push_back();
        _current->frameId = _frame_id;
    }

    TankAction* _CreateAction(uint class_type)
    {
        switch (class_type)
        {
        case action_Input:
            return new TankInputAction();
        }
        return NULL;
    }

    void _OnSerialize(BinaryEncoder& se, const TankFrame& frame) const
    {
        se << frame.frameId << static_cast<uint>(frame.actions.size());
        for (list_t<TankAction*>::const_iterator itr = frame.actions.begin(); itr != frame.actions.end(); ++itr)
        {
            se << (*itr)->action;
            (*itr)->OnSerialize(se);
        }
        se << frame.snapshoot;
    }

    void _OnDeserialize(BinaryDecoder& se, TankFrame& frame)
    {
        uint count = 0;
        se << frame.frameId << count;
        for (uint i = 0; i < count; ++i)
        {
            uint class_type = action_Known;
            se << class_type;

            TankAction* action = _CreateAction(class_type);
            if (action)
            {
                action->action = class_type;
                action->OnDeserialize(se);
            }
        }
        se << frame.snapshoot;
    }
public:
    TankRecorder(uint snapshoot_step = 100) : _frame_id(0), _snapshoot_step(snapshoot_step)
    {
        _yield();
    }
    virtual ~TankRecorder()
    {
        Clear();
    }

    void Clear()
    {
        for (list_t<TankFrame>::iterator itr = _frames.begin(); itr != _frames.end(); ++itr)
        {
            for (list_t<TankAction*>::iterator a_itr = itr->actions.begin(); a_itr != itr->actions.end(); ++a_itr)
            {
                delete (*a_itr);
            }
        }
        _frames.clear();
        _frame_id = 0;
        _yield();
    }
    void AcceptInput(TankGame& game, uint player_id, byte direction, byte key_bit_code)
    {
        TankPlayer* player = game.AcceptInput(player_id, direction, key_bit_code);
        if (player)
        {
            TankInputAction* a = (TankInputAction*)_CreateAction(action_Input);
            a->tank_uid = player->Uid;
            a->direction = direction;
            a->key_bit_code = key_bit_code;
            _current->actions.push_back(a);
        }
    }
    void Record(TankGame& game)
    {
        if (_frame_id % _snapshoot_step == 0)
        {
            TankGame::SaveSnapshoot(game, _current->snapshoot);
        }

        _frame_id += 1;
        _yield();
    }
    /// 返回当前已录制的最大帧
    inline uint GetCurrentFrameId() const
    {
        return _frame_id;
    }
    /**
     * @brief 跳帧,返回最近带有快照的帧数据
     * @param [in] uint frame_id 期望跳转的帧数
     * @return const zhou_yb::application::game::tank90::TankFrame&
     */
    const TankFrame& Seek(uint frame_id)
    {
        frame_id = _min(frame_id, _frame_id);
        // 加载到最近的快照
        uint snapshoot_frame_id = static_cast<uint>(frame_id / _snapshoot_step);
        _replay = _frames.begin();
        for (uint i = 0; i < snapshoot_frame_id; ++i)
            ++_replay;
        return (*_replay);
    }
    /**
     * @brief 沿Seek跳转后的帧开始播放
     * @param [in] TankGame & game 需要代理的Game对象
     * @param [in] uint step 每次播放的倍数(速度)
     * @return bool 是否播放到当前最新的帧
     */
    bool Replay(TankGame& game, uint step = 1, uint* frameId = NULL)
    {
        for (uint i = 0; i < step; ++i)
        {
            if (_replay->frameId >= _frame_id)
                return false;
            for (list_t<TankAction*>::iterator itr = _replay->actions.begin(); itr != _replay->actions.end(); ++itr)
            {
                (*itr)->DoAction(game);
            }
            if (frameId)
            {
                *frameId = _replay->frameId;
            }
            game.Update();
            ++_replay;
        }
        return true;
    }
};
//--------------------------------------------------------- 
} // namespace tank90
} // namespace game
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
 #endif // #ifndef _LIBZHOUYB_TANK_H_
//========================================================= 